import { extend } from "../utils";

const targetMap = new Map();

class NativeEffect {
  deps: Set<this>[] = [];
  active: boolean = true;
  onStop?: () => void | undefined;

  constructor(
    private readonly effectFn: () => void,
    public scheduler?: () => any | undefined,
  ) {
    this.effectFn = effectFn;
    this.scheduler = scheduler;
  }

  run() {
    activeEffect = this;
    return this.effectFn();
  }

  stop() {
    if (this.active) {
      this.onStop?.();
      cleanupEffective(this);
      this.active = false;
    }
  }
}

// 清楚effect
function cleanupEffective(effect: NativeEffect) {
  effect.deps.forEach((dep) => {
    dep.delete(effect);
  });
}

// 存储目标对象的每个键值effect
export function track(target: object, key: string | symbol) {
  // 存储结构 target -> key -> dep
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  let dep = depsMap.get(key);
  if (!dep) {
    depsMap.set(key, (dep = new Set()));
  }
  if (!activeEffect) return;

  dep.add(activeEffect);
  // 将存储dep的集合存储到当前activeEffect的属性上面
  activeEffect.deps.push(dep);
}

// 触发每个target中键值存储的effect
export function trigger(target: object, key: string | symbol) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  const dep = depsMap.get(key);
  for (const effect of dep) {
    // 如果这个effect对象存在scheduler属性则在更新属性时调用这个scheduler
    if (effect.scheduler) {
      effect.scheduler();
    } else {
      effect.run();
    }
  }
}

let activeEffect: NativeEffect;

interface EffectOption {
  scheduler?: () => void | undefined;
  onStop?: () => void | undefined;
}

export function effect(fn: () => void, option?: EffectOption | undefined) {
  const _effect = new NativeEffect(fn, option?.scheduler);
  // 将effect和options中的属性进行合并
  extend(_effect, option);

  _effect.run();

  const runner: any = _effect.run.bind(_effect);
  runner.effect = _effect;
  return runner;
}

export function stop(runner: any) {
  runner.effect.stop();
}
